package main

import (
	"time"

	"github.com/Logicalis/asn1"
)

type AllDataDO struct {
	DO0 bool `asn1:"tag:3"`
	DO1 bool `asn1:"tag:3"`
	DO2 bool `asn1:"tag:3"`
	DO3 bool `asn1:"tag:3"`
	DO4 bool `asn1:"tag:3"`
	DO5 bool `asn1:"tag:3"`
	DO6 bool `asn1:"tag:3"`
	DO7 bool `asn1:"tag:3"`
}

type GooseDOApp struct {
	GocbRef            string    `asn1:"tag:0"`
	TimeToLive         int       `asn1:"tag:1"`
	DataSet            string    `asn1:"tag:2"`
	GoID               string    `asn1:"optional,tag:3"`
	UtcTime            [8]byte   `asn1:"tag:4"`
	StNum              int       `asn1:"tag:5"`
	SqNum              int       `asn1:"tag:6"`
	Test               bool      `asn1:"tag:7"`
	ConfRev            int       `asn1:"tag:8"`
	NeedsCommissioning bool      `asn1:"tag:9"`
	NumDataSetEntries  int       `asn1:"tag:10"`
	AllData            AllDataDO `asn1:"tag:11"`
}

type AllDataDI struct {
	DI0  bool `asn1:"tag:3"`
	DI1  bool `asn1:"tag:3"`
	DI2  bool `asn1:"tag:3"`
	DI3  bool `asn1:"tag:3"`
	DI4  bool `asn1:"tag:3"`
	DI5  bool `asn1:"tag:3"`
	DI6  bool `asn1:"tag:3"`
	DI7  bool `asn1:"tag:3"`
	DI8  bool `asn1:"tag:3"`
	DI9  bool `asn1:"tag:3"`
	DI10 bool `asn1:"tag:3"`
	DI11 bool `asn1:"tag:3"`
	DI12 bool `asn1:"tag:3"`
	DI13 bool `asn1:"tag:3"`
	DI14 bool `asn1:"tag:3"`
	DI15 bool `asn1:"tag:3"`
}

type GooseDIApp struct {
	GocbRef            string    `asn1:"tag:0"`
	TimeToLive         int       `asn1:"tag:1"`
	DataSet            string    `asn1:"tag:2"`
	GoID               string    `asn1:"optional,tag:3"`
	UtcTime            [8]byte   `asn1:"tag:4"`
	StNum              int       `asn1:"tag:5"`
	SqNum              int       `asn1:"tag:6"`
	Test               bool      `asn1:"tag:7"`
	ConfRev            int       `asn1:"tag:8"`
	NeedsCommissioning bool      `asn1:"tag:9"`
	NumDataSetEntries  int       `asn1:"tag:10"`
	AllData            AllDataDI `asn1:"tag:11"`
}

type GooseCtrl struct {
	stat         int
	retrans      []time.Duration
	retrIdx      int
	chgCount     int
	allData      AllDataDO
	lastSendTm   time.Time
	lastChangeTm time.Time
}

func NewGooseCtrl(allData *AllDataDO) *GooseCtrl {
	var gs GooseCtrl
	gs.stat = 0
	gs.retrans = []time.Duration{2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 5000}
	gs.retrIdx = 0
	gs.chgCount = 0
	gs.allData = *allData
	gs.lastSendTm = time.Now()
	gs.lastChangeTm = gs.lastSendTm
	return &gs
}

func (gs *GooseCtrl) PollStatChange() (changed bool, timeout bool) {
	var timeNow = time.Now()
	changed = false
	timeout = false
	if timeNow.Sub(gs.lastChangeTm) > 2*time.Second { // 定期变化输出量
		if gs.chgCount%12 < 6 {
			if gs.chgCount%2 == 0 {
				gs.allData.DO0 = true
			} else {
				gs.allData.DO0 = false
			}
		} else {
			if gs.chgCount%2 == 0 {
				gs.allData.DO1 = true
			} else {
				gs.allData.DO1 = false
			}
		}
		gs.chgCount += 1
		gs.retrIdx = 0
		changed = true
		gs.lastSendTm = timeNow
		gs.lastChangeTm = timeNow
	} else if timeNow.Sub(gs.lastSendTm) > gs.retrans[gs.retrIdx]*time.Millisecond {
		if gs.retrIdx < len(gs.retrans)-1 {
			gs.retrIdx += 1
		}
		timeout = true
		gs.lastSendTm = timeNow
	}
	return changed, timeout
}

func EncodeGoose(GoApp *GooseDOApp) ([]byte, error) {
	ctx := asn1.NewContext()
	// Use BER for encoding and decoding.
	ctx.SetDer(false, false)

	goapdu, err := ctx.EncodeWithOptions(*GoApp, "application,tag:1")
	return goapdu, err
}

func DecodeGoose(apdu []byte) (*GooseDIApp, error) {
	ctx := asn1.NewContext()
	// Use BER for encoding and decoding.
	ctx.SetDer(false, false)

	godec := GooseDIApp{}

	_, err := ctx.DecodeWithOptions(apdu, &godec, "application,tag:1")

	return &godec, err
}
